/* 
 * WTF Deferred Lighting & SSAO Fragmnet Shader
 *
 * TODO: Fix specular shading
 */

// Buffers
uniform sampler2DRect diffuseTex;
uniform sampler2DRect normalDepthTex;
uniform sampler2DRect specularTex;
uniform sampler2D rnmTex;

varying vec2 uv;
varying vec2 uvn;

// SSAO Parameters
#define SAMPLES 10                      // samples to use, either 10 (default) or 16
const float Strength = 1.0;
const float Offset = 5.0;               // random normals offset
const float Falloff = 0.00000005;         // occlusion function fall-off, higher => faster fall-off
const float Radius = 500.0;               // Sampling radius
const float InvSamples = 0.07;         // effect strength, more negative => starker

// returns normals in screenspace and linear depth (ideally)
void sampNormalDepth(in vec2 coord, out vec3 normal, out float depth)
{
    vec4 samp = texture2DRect(normalDepthTex, coord);
    normal = samp.xyz;
    depth = samp.a;
}

// occlusion function
float occlusionFunction(in float depthDifference, in float weight) 
{
    return step(Falloff, depthDifference) * weight * (1.0 - smoothstep(Falloff, Strength, depthDifference));
}

// depth adjustion
float depthAdjust(in float depth) 
{
    return Radius/depth;
}

// unpack depth, must be an inverse of depth-packing from deferred shading preprocessor
float depthUnpack(in float depth) {
	return depth;
}

void getSpecularProperties(out vec3 spec, out float shin) 
{
    vec4 samp = texture2DRect(diffuseTex, uv);
    spec = samp.xyz;
    shin = samp.a;
}

void main(void) 
{
    // I. DATA PHASE
    // get the normal and depth of current fragment in SS
    vec3 norm;
    float depth;
    sampNormalDepth(uv, norm, depth);
    
    // II. DEFERRED SHADING PHASE
    // diffuse shading
    // currently, just fix the light w.r.t the camera
    float diffuse, ambient = 0.3, shininess = 1.0;
    vec3 inc = vec3(0.0), specular = vec3(0.0);
    vec3 dlight = vec3(0.0, 2.0, -4.0);  // light offset from the camera
    
    inc = vec3(uvn.x - 0.5, -uvn.y - 0.5, -depth) + dlight;                 // incident ray (from light to fragment) in SS
    diffuse = pow(max(dot(inc, norm), 0.0) / length(inc) * 2.5, 1.5);
    
    // specular shading
    getSpecularProperties(specular, shininess);
    if (diffuse > 0.0) {
        specular = pow(max(dot(normalize(inc - dlight / 2.0), norm), 0.0), shininess) * specular;
    }

    // III. SSAO PHASE
    // these are the random vectors inside a unit sphere
#if SAMPLES == 16
    vec3 pSphere[16] = vec3[16](
        vec3(0.53812504, 0.18565957, -0.43192),
        vec3(0.13790712, 0.24864247, 0.44301823),
        vec3(0.33715037, 0.56794053, -0.005789503),
        vec3(-0.6999805, -0.04511441, -0.0019965635),
        vec3(0.06896307, -0.15983082, -0.85477847),
        vec3(0.056099437, 0.006954967, -0.1843352),
        vec3(-0.014653638, 0.14027752, 0.0762037),
        vec3(0.010019933, -0.1924225, -0.034443386),
        vec3(-0.35775623, -0.5301969, -0.43581226),
        vec3(-0.3169221, 0.106360726, 0.015860917),
        vec3(0.010350345, -0.58698344, 0.0046293875),
        vec3(-0.08972908, -0.49408212, 0.3287904),
        vec3(0.7119986, -0.0154690035, -0.09183723),
        vec3(-0.053382345, 0.059675813, -0.5411899),
        vec3(0.035267662, -0.063188605, 0.54602677),
        vec3(-0.47761092, 0.2847911, -0.0271716));
#else 
    vec3 pSphere[10] = vec3[10](
        vec3(-0.010735935, 0.01647018, 0.0062425877),
        vec3(-0.06533369, 0.3647007, -0.13746321),
        vec3(-0.6539235, -0.016726388, -0.53000957),
        vec3(0.40958285, 0.0052428036, -0.5591124),
        vec3(-0.1465366, 0.09899267, 0.15571679),
        vec3(-0.44122112, -0.5458797, 0.04912532),
        vec3(0.03755566, -0.10961345, -0.33040273),
        vec3(0.019100213, 0.29652783, 0.066237666),
        vec3(0.8765323, 0.011236004, 0.28265962),
        vec3(0.29264435, -0.40794238, 0.15964167));
#endif    
    
 
    // grab a normal for reflecting the sample rays later on
    vec3 fres = normalize((texture2D(rnmTex,uvn * Offset).xyz * 2.0) - vec3(1.0));
   
    
    // prepare for hemisphere sampling
    float occluderDepth, depthDifference, accOcclusion;
    vec4 occluderSample;
    vec3 ray, occluderNormal;
    for(int i = 0; i < 0; ++i)
    {
        // get a vector (randomized inside of a sphere with radius 1.0) from a texture and reflect it
        //ray = depthAdjust(depth)*reflect(pSphere[i],fres);
        ray = depthAdjust(depth) * pSphere[i];
 
        // get the depth of the occluder fragment, calculate depth difference
        sampNormalDepth(uv.xy + sign(dot(ray,norm)) * ray.xy, occluderNormal, occluderDepth);
        depthDifference = (occluderDepth - depth);  // if depthDifference is negative = occluder is behind current fragment
 
        // apply occlusion function
        accOcclusion += occlusionFunction(depthDifference, (dot(occluderNormal,norm)));
    }
    float occlusion = (accOcclusion * InvSamples);
 
    
    // IV. OUTPUT PHASE
    gl_FragColor.rgb = 
        (diffuse * 0.7 + ambient * 1.5 + specular) * texture2DRect(diffuseTex, uv).rgb; // */
        
    // DEBUG VIEWS
    // gl_FragColor.rg = uvn;                       // Displays normalized UVs
    // gl_FragColor.rgb = norm * 0.5 + 0.5;         // Displays normals, packed to RGB cube
    // gl_FragColor.rgb = inc * 0.5 + 0.5;          // Display incident ray
    // gl_FragColor.rgb = vec3((depthDifference / 10.0));              // Depths view
    // gl_FragColor.rgb = vec3(diffuse);              // Diffuse term view
    // gl_FragColor.rgb = vec3(occlusion); // AO only
    // gl_FragColor.rgb = occluderNormal * 0.5 + 0.5; // last sampled normal
    // gl_FragColor.rgb = vec3(depthDifference) * 10.0; // depth difference
}

